package com.xq.springboot.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.xq.springboot.realm.UserRealm;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author xq
 * @version 1.0
 * @date 2021/4/19 9:24
 * @description shiro自动配置类
 */
@Configuration
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroAutoConfiguration {
    @Autowired
    private ShiroProperties shiroProperties;

    /**
     * 创建凭证匹配器对象（CredentialsMatcher）
     * @return
     */
    @Bean
    public HashedCredentialsMatcher credentialsMatcher() {
        // 创建凭证匹配器对象
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        // 配置加密方式和散列次数
        credentialsMatcher.setHashAlgorithmName(shiroProperties.getHashAlgorithmName());
        credentialsMatcher.setHashIterations(shiroProperties.getHashIterations());
        return credentialsMatcher;
    }

    /**
     * 创建用户的授权领域类（UserRealm）
     * @param credentialsMatcher 凭证匹配器对象
     * @return
     */
    @Bean
    public UserRealm userRealm(CredentialsMatcher credentialsMatcher) {
        UserRealm userRealm = new UserRealm();
        // 注入凭证匹配器对象
        userRealm.setCredentialsMatcher(credentialsMatcher);
        return userRealm;
    }

    /**
     * 创建安全管理器（SecurityManager）
     * @param userRealm
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(UserRealm userRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 注入用户授权领域
        securityManager.setRealm(userRealm);
        return securityManager;
    }

    /**
     * 创建Shiro的过滤器 （Filter）
     * id和web.xml里面的shiroFilter的targetBeanName的值要一致
     * @param securityManager
     * @return
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        // 创建Shiro的过滤器对象
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 获取放行的路径
        String[] anonUrls = shiroProperties.getAnonUrls();
        // 获取登出的路径
        String logoutUrl = shiroProperties.getLogoutUrl();
        // 获取拦截的路径
        String[] authcUrls = shiroProperties.getAuthcUrls();

        // 注入安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 注入登录页Url
        shiroFilterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());
        // 注入未授权的页面Url
        shiroFilterFactoryBean.setUnauthorizedUrl(shiroFilterFactoryBean.getUnauthorizedUrl());
        // 创建过滤器Map容器
        Map<String, String> filterChainDefinitionMap = new HashMap<>(16);
        // 判断放行的Url不为空和放行的Url的数组长度要大于零
        if (anonUrls != null && anonUrls.length > 0) {
            for (String anonUrl : anonUrls) {
                // 注入放行的Url到过滤器容器中
                filterChainDefinitionMap.put(anonUrl, "authc");
            }
        }
        // 判断登出的Url不为空
        if (logoutUrl != null) {
            // 注入登出的Url到过滤器容器中
            filterChainDefinitionMap.put(logoutUrl,"logout");
        }
        // 判断拦截的Url是否为空和拦截的Url的数组长度要大于零
        if (authcUrls != null && authcUrls.length > 0) {
            for (String authcUrl : authcUrls) {
                // 注入拦截的Url到过滤器容器中
                filterChainDefinitionMap.put(authcUrl, "authc");
            }
        }
        return shiroFilterFactoryBean;
    }

    /**
     * 注册过滤器（filter）
     * @return
     */
    @Bean
    public FilterRegistrationBean<DelegatingFilterProxy> filterProxyFilterRegistrationBean() {
        // 创建注册过滤器对象
        FilterRegistrationBean<DelegatingFilterProxy> registrationBean = new FilterRegistrationBean<>();
        // 创建过滤器代理对象
        DelegatingFilterProxy filterProxy = new DelegatingFilterProxy();
        // 注入注册过滤器对象
        registrationBean.setFilter(filterProxy);
        // 过滤器相关配置 targetFilterLifecycle:目标过滤器的生命周期和targetBeanName:Filter名称
        registrationBean.addInitParameter("targetFilterLifecycle","true");
        registrationBean.addInitParameter("targetBeanName","shiroFilter");

        // 注入Servlet的名称
        List<String> servletNames = new ArrayList<>();
        servletNames.add(DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
        registrationBean.setServletNames(servletNames);
        return registrationBean;
    }

        /**
         * 创建Shiro方言对象
     * 创建这个对象，可以在html页面引用shiro标签，
     * 上面两个方法必须添加，不然会报错
     * @return
         */
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    /*加入注解的使用，不加入这个注解不生效--开始*/
    /**
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator=new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    /*加入注解的使用，不加入这个注解不生效--结束*/
}
